﻿// ===============================================================================
// XCI.NET 西安交通信息WinForm开发框架 2018 版权所有
// ===============================================================================
using DevExpress.Utils;
using DevExpress.Utils.Drawing;
using DevExpress.Utils.Menu;
using DevExpress.XtraBars;
using DevExpress.XtraTreeList;
using DevExpress.XtraTreeList.Nodes;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using XCI.Core;
using XCI.Data.Entity;
using XCI.Extensions;
using XCI.Helper;
using XCI.Windows.Controls;
using XCI.Windows.Plugin.GridSetting;
using XCI.Windows.Plugin.GridSetting.UI;

namespace XCI.Windows.Helper
{
    /// <summary>
    /// 树控件操作帮助类
    /// </summary>
    /// <typeparam name="T">对象类型</typeparam>
    public class TreeHelper<T> where T : class, new()
    {
        public static string EmptyDataMessage = "暂未查到相关数据";
        private readonly Dictionary<string, PrimaryKeyValue> _changedDatas = new Dictionary<string, PrimaryKeyValue>();
        //private readonly Dictionary<string, string> _checkedKeyDatas = new Dictionary<string, string>();
        //private bool _isCheckedAll;
        private TreeListNode dragNode;
        //private IList _dataList;
        private object _focusNodeId;
        private List<object> _expandNodeIds;
        private List<object> _checkedNodeIds;

        private BackgroundWorker _worker;
        private Action<DoWorkEventArgs> _runAction;
        private Action<RunWorkerCompletedEventArgs> _errorAction;
        private Action<RunWorkerCompletedEventArgs> _completAction;
        private readonly Stopwatch sw = new Stopwatch();
        //private readonly LoadMask loadMask = new LoadMask();
        private bool dataLoding = false;

        /// <summary>
        /// 树控件操作类
        /// </summary>
        /// <param name="tree">树控件</param>
        public TreeHelper(TreeList tree)
        {
            string id = null;
            TreeListx itree = tree as TreeListx;
            if (itree != null)
            {
                id = itree.Id;
            }
            Init(id, tree);
        }

        /// <summary>
        /// 树控件
        /// </summary>
        public TreeList Tree { get; set; }

        /// <summary>
        /// 对象元数据
        /// </summary>
        public EntityData Metadata { get; protected set; }


        #region Set

        /// <summary>
        /// 控件标识
        /// </summary>
        public string Id { get; set; }

        /// <summary>
        /// 监控列值变化
        /// </summary>
        public bool MonitorCellValueChanged { get; set; }

        /// <summary>
        /// 数据为空时绘制空数据提示消息
        /// </summary>
        public bool DrawEmptyMessage { get; set; } = true;

        /// <summary>
        /// 自动生成右键菜单
        /// </summary>
        public bool AutoPopupMenu { get; set; } = true;

        /// <summary>
        /// 右键菜单
        /// </summary>
        public PopupMenu Popup { get; set; }

        /// <summary>
        /// 自动勾选父节点
        /// </summary>
        public bool AutoCheckedParentNode { get; set; }

        /// <summary>
        /// 自动勾选子节点
        /// </summary>
        public bool AutoCheckedChildNode { get; set; }

        /// <summary>
        /// 是否设置选中行样式
        /// </summary>
        public bool IsApplyCheckedRowStyle { get; set; }

        /// <summary>
        /// 是否保留节点焦点状态
        /// </summary>
        public bool IsReserveNodeFocused { get; set; }

        /// <summary>
        /// 是否保留节点展开状态
        /// </summary>
        public bool IsReserveNodeExpandedStatus { get; set; }

        /// <summary>
        /// 是否保留节点勾选状态
        /// </summary>
        public bool IsReserveNodeCheckedStatus { get; set; }

        /// <summary>
        /// 执行耗时显示控件
        /// </summary>
        public BarItem ElapsedTimeItem { get; set; }

        #endregion

        #region Calc

        /// <summary>
        /// 获取线程执行对象。
        /// </summary>
        public BackgroundWorker Worker => _worker;

        /// <summary>
        /// 选中的主键
        /// </summary>
        public string SelectedId => Tree.FocusedNode != null ? GetRecordId(Tree.FocusedNode) : string.Empty;

        /// <summary>
        /// 选中的对象
        /// </summary>
        public T Selected => Tree.FocusedNode != null ? Get(Tree.FocusedNode) : default(T);

        /// <summary>
        /// 选中的父节点对象主键
        /// </summary>
        public string SelectedParentId => Tree.FocusedNode != null ? GetRecordId(Tree.FocusedNode.ParentNode) : string.Empty;

        /// <summary>
        /// 选中的父节点对象
        /// </summary>
        public T SelectedParent => Tree.FocusedNode != null ? GetParent(Tree.FocusedNode) : default(T);

        /// <summary>
        /// 获取选中的对象列表
        /// </summary>
        public List<T> SelectedList
        {
            get
            {
                var entityList = new List<T>();
                var selectedNodes = Tree.Selection;
                int selectedCount = selectedNodes.Count;
                if (selectedCount == 0)
                {
                    return null;
                }
                for (int i = 0; i < selectedCount; i++)
                {
                    entityList.Add(Get(selectedNodes[i]));
                }
                return entityList;
            }
        }

        /// <summary>
        /// 获取选中的主键数组
        /// </summary>
        public string[] SelectedIdList => GetSelectedIds();

        /// <summary>
        /// 获取选中的主键字符串,用逗号分开
        /// </summary>
        public string SelectedIds => GetSelectedIds().JoinString();

        /// <summary>
        /// 获取勾选的对象列表
        /// </summary>
        public List<T> CheckedList
        {
            get
            {
                var entityList = new List<T>();
                var selectedNodes = GetCheckedNodes();
                int selectedCount = selectedNodes.Count;
                if (selectedCount == 0)
                {
                    return null;
                }
                for (int i = 0; i < selectedCount; i++)
                {
                    entityList.Add(Get(selectedNodes[i]));
                }
                return entityList;
            }
        }

        /// <summary>
        /// 获取勾选的主键数组
        /// </summary>
        public string[] CheckedIdList => GetCheckedIds();

        /// <summary>
        /// 获取勾选的主键字符串,用逗号分开
        /// </summary>
        public string CheckedIds => GetCheckedIds().JoinString();

        /// <summary>
        /// 是否是当前层级结构中的第一个节点
        /// </summary>
        public bool IsLevelFirstNode
        {
            get
            {
                if (Tree.FocusedNode != null)
                {
                    return Tree.GetNodeIndex(Tree.FocusedNode) == 0;
                }
                return false;
            }
        }

        /// <summary>
        /// 是否是当前层级结构中的最后一个节点
        /// </summary>
        public bool IsLevelLastNode
        {
            get
            {
                if (Tree.FocusedNode != null)
                {
                    var node = Tree.FocusedNode;
                    int index = Tree.GetNodeIndex(node);
                    int count = GetParentNodeChildNodeCount(node);
                    if (index == count - 1)
                    {
                        return true;
                    }
                }
                return false;
            }
        }

        /// <summary>
        /// 是否包含数据
        /// </summary>
        public bool HasData => Tree.Nodes.Count > 0;

        /// <summary>
        /// 是否选中数据
        /// </summary>
        public bool HasSelected => Tree.FocusedNode != null;

        /// <summary>
        /// 当前是选中多条
        /// </summary>
        public bool HasMultiSelected => Tree.Selection.Count > 1;

        /// <summary>
        /// 当前是勾选多条
        /// </summary>
        public bool HasMultiChecked => Tree.GetAllCheckedNodes().Count > 1;

        /// <summary>
        /// 是否勾选数据
        /// </summary>
        public bool HasChecked => Tree.GetNodeList().Any(p => p.Checked);

        /// <summary>
        /// 是否修改数据
        /// </summary>
        public bool HasDataChanged => _changedDatas.Count > 0;

        /// <summary>
        /// 数据加载耗时
        /// </summary>
        public TimeSpan WorkElapsedTime { get; private set; }

        #endregion

        #region 公共事件

        /// <summary>
        /// 节点勾选事件
        /// </summary>
        public event EventHandler<EventArgs> NodeChecked;
        protected void OnNodeChecked(EventArgs e)
        {
            NodeChecked?.Invoke(this, e);
        }

        /// <summary>
        /// 修改数据事件
        /// </summary>
        public event EventHandler<EventArgs> ModifyData;

        protected void OnModifyData(EventArgs e)
        {
            ModifyData?.Invoke(this, e);
        }

        #endregion

        #region 公共方法

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="id">控件标识</param>
        /// <param name="tree">树控件</param>
        private void Init(string id, TreeList tree)
        {
            this.Id = id;
            this.Tree = tree;

            if (Tree == null) throw new ArgumentNullException(nameof(Tree));

            this.Metadata = EntityHelper.Get(typeof(T));

            #region TreeSettingPlugin

            InitSettingPlugin();

            #endregion


            Tree.ViewInfo.PaintAppearance.HideSelectionRow.ForeColor = Tree.ViewInfo.PaintAppearance.FocusedRow.ForeColor;
            Tree.ViewInfo.PaintAppearance.HideSelectionRow.BackColor = Tree.ViewInfo.PaintAppearance.FocusedRow.BackColor;

            if (DrawEmptyMessage)
            {
                Tree.CustomDrawEmptyArea += (o, e) =>
                {
                    if (dataLoding || Tree.Nodes.Count > 0) return;
                    //字体
                    Font f = new Font(AppearanceObject.DefaultFont.Name, 16,FontStyle.Regular);
                    Rectangle r = new Rectangle(Tree.Width / 2 - 100, e.Bounds.Top + 45, e.Bounds.Right - 5, e.Bounds.Height - 5);
                    e.Cache.DrawString(EmptyDataMessage, f, e.Cache.GetSolidBrush(Color.Gray), r);
                    e.Handled = true;
                };
            }

            Tree.NodeCellStyle += (o, e) =>
            {
                if (IsApplyCheckedRowStyle && e.Node.Checked)
                {
                    e.Appearance.BackColor = Tree.ViewInfo.PaintAppearance.FocusedRow.BackColor;
                    e.Appearance.ForeColor = Tree.ViewInfo.PaintAppearance.FocusedRow.ForeColor;
                }
            };

            #region CustomDrawRowIndicator

            if (Tree.OptionsView.ShowIndicator)
            {
                Tree.CustomDrawNodeIndicator += (o, e) =>
                {
                    if (!e.IsNodeIndicator) return;
                    var t = (DevExpress.XtraTreeList.TreeList)o;
                    var args = (IndicatorObjectInfoArgs)e.ObjectArgs;
                    var index = t.GetVisibleIndexByNode(e.Node);
                    args.DisplayText = index < 0 ? string.Empty : (index + 1).ToString();
                    e.ImageIndex = -1;
                };
            }

            #endregion

            #region MouseUp

            Tree.MouseUp += (o, e) =>
            {
                if (Popup != null)
                {
                    TreeListHitInfo hi = Tree.CalcHitInfo(e.Location);
                    if (hi.HitInfoType == HitInfoType.Cell && hi.Node != null)
                    {
                        Tree.FocusedNode = hi.Node;
                    }

                    if (e.Button == MouseButtons.Right &&
                            ((hi.HitInfoType == HitInfoType.Cell)
                             || (hi.HitInfoType == HitInfoType.Empty))
                        )
                    {
                        Popup.ShowPopup(Control.MousePosition);
                    }

                }
            };

            #endregion


            //#region BarControllerFont

            //var menuManager = Tree.MenuManager;
            //if (menuManager != null)
            //{
            //    //设置菜单字体样式
            //    var barManager = (BarManager)menuManager;
            //    barManager.Controller.PropertiesBar.LargeIcons = WindowsSetting.Instance.PageToolbarLargeIcons;
            //    barManager.Controller.PropertiesBar.LargeIconsInMenu = DefaultBoolean.False;
            //}

            //#endregion

            #region AutoBuildPopupMenu

            AutoBuildPopupMenuCore();

            #endregion

            #region MonitorCellValueChanged

            if (MonitorCellValueChanged)
            {
                Tree.CellValueChanged += Tree_CellValueChanged;
            }

            #endregion

            Tree.AfterCheckNode += Tree_AfterCheckNode;
            Tree.BeforeDragNode += Tree_BeforeDragNode;
            Tree.AfterDragNode += Tree_AfterDragNode;

            if (Metadata != null)
            {
                tree.KeyFieldName = Metadata.PrimaryProperty?.Name;
                tree.ParentFieldName = Metadata.ParentProperty?.Name;
            }

            #region Worker
            InitWorker();
            #endregion
        }

        private void InitWorker()
        {
            _worker = new BackgroundWorker();
            _worker.WorkerReportsProgress = false;
            _worker.WorkerSupportsCancellation = false;
            _worker.DoWork += Worker_DoWork;
            _worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
        }

        private void Worker_DoWork(object sender, DoWorkEventArgs e)
        {
            dataLoding = true;
            sw.Reset();
            sw.Start();
            _runAction?.Invoke(e);
            sw.Stop();
            WorkElapsedTime = sw.Elapsed;
        }

        private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                if (_errorAction != null)
                {
                    _errorAction?.Invoke(e);
                }
                else
                {
                    Tree.HideLoadingPanel();
                    MessageBoxHelper.ShowError(DebugHelper.BuildAllMessage(e.Error));
                }
            }
            else
            {
                if (ElapsedTimeItem != null)
                {
                    ElapsedTimeItem.Caption = $"耗时:{WorkElapsedTime.TotalMilliseconds:###0.##} ms";
                }
                SetDataSource(e.Result);
                _completAction?.Invoke(e);
            }
            dataLoding = false;
            Tree.HideLoadingPanel();
        }

        /// <summary>
        /// 设置异步执行回调函数
        /// </summary>
        /// <param name="runAction">回调函数</param>
        public TreeHelper<T> RunCallback(Action<DoWorkEventArgs> runAction)
        {
            _runAction = runAction;
            return this;
        }

        /// <summary>
        /// 设置后台操作异常回调函数。
        /// </summary>
        /// <param name="errorAction">回调函数</param>
        public TreeHelper<T> ErrorCallback(Action<RunWorkerCompletedEventArgs> errorAction)
        {
            _errorAction = errorAction;
            return this;
        }

        /// <summary>
        /// 设置后台操作已完成、 已取消或引发异常时回调函数。
        /// </summary>
        /// <param name="completAction">回调函数</param>
        public TreeHelper<T> CompletCallback(Action<RunWorkerCompletedEventArgs> completAction)
        {
            _completAction = completAction;
            return this;
        }

        /// <summary>
        /// 开始执行后台操作。
        /// </summary>
        public void RunAsync()
        {
            if (_worker.IsBusy)
            {
                return;
            }
            Tree.ShowLoadingPanel();
            _worker.RunWorkerAsync();
        }

        /// <summary>
        /// 请求取消的挂起的后台操作。
        /// </summary>
        public void CancelAsync()
        {
            if (_worker.IsBusy)
            {
                _worker.CancelAsync();
            }
        }

        /// <summary>
        /// 导出方法
        /// </summary>
        public void Export()
        {
            Tree.ShowRibbonPrintPreview();
        }

        /// <summary>
        /// 删除选中的数据
        /// </summary>
        public void DeleteByChecked(Action action, string confirmMessage = null)
        {
            var selectedIds = GetCheckedIds();
            int count = selectedIds.Length;
            if (count == 0)
            {
                MessageBoxHelper.ShowError("请至少选中一条数据");
                return;
            }
            if (string.IsNullOrEmpty(confirmMessage))
            {
                confirmMessage = $"确定要删除选中的 {count} 条数据吗?";
            }
            if (MessageBoxHelper.ShowWarnYesNo(confirmMessage) != DialogResult.Yes)
            {
                return;
            }

            action?.Invoke();
        }

        /// <summary>
        /// 删除选中的数据
        /// </summary>
        public void DeleteBySelected(Action action, string confirmMessage = null)
        {
            int count = Tree.Selection.Count;
            if (count == 0)
            {
                MessageBoxHelper.ShowError("请至少选中一条数据");
                return;
            }
            if (string.IsNullOrEmpty(confirmMessage))
            {
                confirmMessage = $"确定要删除选中的 {count} 条数据吗?";
            }
            if (MessageBoxHelper.ShowWarnYesNo(confirmMessage) != DialogResult.Yes)
            {
                return;
            }

            action?.Invoke();
        }

        /// <summary>
        /// 设置数据源
        /// </summary>
        /// <param name="dataSource">数据源</param>
        public void SetDataSource(object dataSource)
        {
            //if (dataSource is IList list)
            //{
            //    if (_dataList == null)
            //    {
            //        _dataList = list;
            //        Tree.DataSource = _dataList;
            //    }
            //    else
            //    {
            //        _dataList.Clear();
            //        foreach (var item in list)
            //        {
            //            _dataList.Add(item);
            //        }
            //        Tree.RefreshDataSource();
            //    }
            //}
            //else
            //{
            //    Tree.DataSource = dataSource;
            //}
            if (Tree == null) return;
            if (IsReserveNodeFocused)
            {
                if (Tree.FocusedNode != null)
                {
                    _focusNodeId = Tree.FocusedNode.GetValue(Tree.KeyFieldName);
                }
            }

            if (IsReserveNodeExpandedStatus)
            {
                if (_expandNodeIds == null)
                {
                    _expandNodeIds = new List<object>();
                }

                _expandNodeIds.Clear();
                Tree.GetNodeList().FindAll(n => n.Expanded).ForEach(n =>
                {
                    var idObj = n.GetValue(Tree.KeyFieldName);
                    if (idObj != null)
                    {
                        _expandNodeIds.Add(idObj);
                    }
                });
            }

            if (IsReserveNodeCheckedStatus)
            {
                if (_checkedNodeIds == null)
                {
                    _checkedNodeIds = new List<object>();
                }

                _checkedNodeIds.Clear();
                Tree.GetNodeList().FindAll(n => n.Checked).ForEach(n =>
                {
                    var idObj = n.GetValue(Tree.KeyFieldName);
                    if (idObj != null)
                    {
                        _checkedNodeIds.Add(idObj);
                    }
                });
            }

            Tree.DataSource = dataSource;

            if (IsReserveNodeExpandedStatus)
            {
                _expandNodeIds.ForEach(id =>
                {
                    var node = Tree.FindNodeByKeyID(id);
                    if (node != null)
                    {
                        node.Expanded = true;
                    }
                });
            }

            if (IsReserveNodeCheckedStatus)
            {
                _checkedNodeIds.ForEach(id =>
                {
                    var node = Tree.FindNodeByKeyID(id);
                    if (node != null)
                    {
                        node.Checked = true;
                    }
                });
            }

            if (IsReserveNodeFocused)
            {
                if (_focusNodeId != null)
                {
                    var fnode = Tree.FindNodeByKeyID(_focusNodeId);
                    if (fnode != null)
                    {
                        Tree.SetFocusedNode(fnode);
                    }
                }
            }
        }

        /// <summary>
        /// 自动生成右键菜单
        /// </summary>
        private void AutoBuildPopupMenuCore()
        {
            if (AutoPopupMenu == false) return;
            if (Popup != null) return;
            var menuManager = Tree.MenuManager;
            if (menuManager == null) return;

            Popup = new PopupMenu();
            Popup.Manager = (BarManager)menuManager;
            foreach (BarItem item in Popup.Manager.Items)
            {
                if (!(item is BarButtonItem)) continue;
                Popup.ItemLinks.Add(item);
            }
        }

        /// <summary>
        /// 初始化配置菜单
        /// </summary>
        private void InitSettingPlugin()
        {
            if (string.IsNullOrEmpty(Id)) return;
            var data = GridSettingManager.Instance.LoadSetting(Id);
            SetSetting(data);
            if (string.IsNullOrEmpty(data))
            {
                AutoBuildColumn();
            }
            Tree.PopupMenuShowing += (o, e) =>
            {
                TreeListHitInfo hitInfo = Tree.CalcHitInfo(e.Point);
                if (hitInfo.HitInfoType != HitInfoType.Column) return;
                var menu = e.Menu;
                //var column = hitInfo.Column;
                menu.Items.Add(new DXMenuItem("全部展开", (oo, ex) => { Tree.ExpandAll(); }) { BeginGroup = true });
                menu.Items.Add(new DXMenuItem("全部合上", (oo, ex) => { Tree.CollapseAll(); }));
                //item = new DXMenuItem("全选/清除", (oo, ex) =>
                //{
                //    if (!_isCheckedAll)
                //    {
                //        Tree.CheckAll();
                //        _isCheckedAll = true;
                //    }
                //    else
                //    {
                //        Tree.UncheckAll();
                //        _isCheckedAll = false;
                //    }
                //});
                //item.BeginGroup = true;
                //menu.Items.Add(item);

                //if (column != null)
                //{
                //    DXMenuItem sortItem = new DXMenuItem("清除排序", (oo, ee) =>
                //    {
                //        column.SortDir = SortDir.None;
                //        Tree.RefreshDataSource();
                //    }, null);
                //    sortItem.Enabled = false;
                //    sortItem.BeginGroup = true;
                //    menu.Items.Add(sortItem);
                //    if (column.SortDir != SortDir.None)
                //    {
                //        sortItem.Enabled = true;
                //    }
                //}

                if (Sys.SysRuntime.Setting.AllowGridSetting)
                {
                    var customItem = new DXMenuItem("表格配置", (oo, aa) => ShowSettingForm(),
                    XCI.Sys.Properties.Resources.technology_16x16);
                    customItem.BeginGroup = true;
                    menu.Items.Add(customItem);
                }

            };

            //Tree.KeyDown += (s, e) =>
            //{
            //    if (e.Control && e.Shift && e.Alt && e.KeyCode == Keys.F11)
            //    {
            //        ShowSettingForm();
            //        e.Handled = true;
            //    }
            //};
        }

        /// <summary>
        /// 显示配置窗口
        /// </summary>
        public void ShowSettingForm()
        {
            var settingFrm = new TreeConfigForm();
            settingFrm.Initialize(new Plugin.GridSetting.TreeSettingPlugin(Tree, Id, Metadata));
            settingFrm.ShowDialog();
            settingFrm.Dispose();
        }

        /// <summary>
        /// 获取树控件配置
        /// </summary>
        public string GetSetting()
        {
            return WinHelper.GetTreeSetting(Tree);
        }

        /// <summary>
        /// 应用Tree二进制配置
        /// </summary>
        public void SetSetting(string setting)
        {
            WinHelper.SetTreeSetting(Tree, setting);
        }

        /// <summary>
        /// 自动生成列
        /// </summary>
        public void AutoBuildColumn()
        {
            WinHelper.AutoBuildTreeColumn(Tree, Metadata);
        }

        /// <summary>
        /// 执行双击事件
        /// </summary>
        public void DblClickRow(EventArgs e, Action editAction)
        {
            if (e is MouseEventArgs ev)
            {
                HitInfoType info = Tree.CalcHitInfo(ev.Location).HitInfoType;
                if (ev.Clicks == 2 && ev.Button == MouseButtons.Left &&
                    (info == HitInfoType.ColumnEdge
                     || info == HitInfoType.Button
                     //|| info == HitInfoType.Column
                     || info == HitInfoType.ColumnButton
                     || info == HitInfoType.NodeCheckBox
                     || info == HitInfoType.Empty))
                {
                    return;
                }
            }

            if (editAction != null && Tree.FocusedNode != null)
            {
                Tree.FocusedNode.Expanded = true;
                editAction();
            }
        }

        /// <summary>
        /// 展开第一个节点
        /// </summary>
        public void ExpandFirstNode()
        {
            if (Tree.Nodes.Count > 0)
            {
                Tree.Nodes[0].Expanded = true;
            }
        }

        /// <summary>
        /// 展开全部节点
        /// </summary>
        public void ExpandAll()
        {
            if (Tree.Nodes.Count > 0)
            {
                Tree.ExpandAll();
            }
        }

        /// <summary>
        /// 第一个节点设置焦点
        /// </summary>
        public void FocusFirstNode()
        {
            if (Tree.Nodes.Count > 0)
            {
                Tree.SetFocusedNode(Tree.Nodes[0]);
            }
        }

        /// <summary>
        /// 获取指定节点的父节点的子节点个数(获取指定节点的兄弟节点数量)
        /// </summary>
        /// <param name="node">指定的节点</param>
        protected int GetParentNodeChildNodeCount(TreeListNode node)
        {
            return node.ParentNode?.Nodes.Count ?? Tree.Nodes.Count;
        }

        /// <summary> 
        /// 设置TreeList显示的图标 
        /// </summary> 
        /// <param name="tl">TreeList组件</param> 
        /// <param name="node">当前结点，从根结构递归时此值必须=null</param> 
        /// <param name="nodeIndex">根结点图标(无子结点)</param> 
        /// <param name="parentIndex">有子结点的图标</param> 
        public void SetStateImageIndex(TreeList tl, TreeListNode node, int nodeIndex, int parentIndex)
        {
            if (node == null)
            {
                foreach (TreeListNode N in tl.Nodes)
                    SetStateImageIndex(tl, N, nodeIndex, parentIndex);
            }
            else
            {
                if (node.HasChildren || node.ParentNode == null)
                {
                    //node.SelectImageIndex = parentIndex; 
                    node.StateImageIndex = parentIndex;
                    //node.ImageIndex = parentIndex;
                }
                else
                {
                    //node.SelectImageIndex = nodeIndex; 
                    node.StateImageIndex = nodeIndex;
                    //node.ImageIndex = nodeIndex;
                }

                foreach (TreeListNode N in node.Nodes)
                {
                    SetStateImageIndex(tl, N, nodeIndex, parentIndex);
                }
            }
        }

        #region 复选框

        /// <summary>
        /// 递归选中父节点
        /// </summary>
        /// <param name="node"></param>
        /// <param name="action"></param>
        protected void CheckedParentNodes(TreeListNode node, Action<TreeListNode> action)
        {
            RecursionParentNode(node, p =>
            {
                bool b = false;
                for (int i = 0; i < p.Nodes.Count; i++)
                {
                    CheckState state = p.Nodes[i].CheckState;
                    if (!node.CheckState.Equals(state))
                    {
                        b = true;
                        break;
                    }
                }
                p.CheckState = b ? CheckState.Checked : node.CheckState;
                action?.Invoke(p);
            });
        }

        /// <summary>
        /// 递归选中子节点
        /// </summary>
        /// <param name="node"></param>
        /// <param name="action"></param>
        protected void CheckedChildNodes(TreeListNode node, Action<TreeListNode> action)
        {
            RecursionChildNode(node, p =>
            {
                p.CheckState = node.CheckState;
                action?.Invoke(p);
            });
        }

        /// <summary>
        /// 递归父节点
        /// </summary>
        /// <param name="node"></param>
        /// <param name="action"></param>
        public void RecursionParentNode(TreeListNode node, Action<TreeListNode> action)
        {
            if (node.ParentNode == null) return;
            var parentNode = node.ParentNode;
            action(parentNode);
            RecursionParentNode(parentNode, action);
        }

        /// <summary>
        /// 递归子节点
        /// </summary>
        /// <param name="node"></param>
        /// <param name="action"></param>
        public void RecursionChildNode(TreeListNode node, Action<TreeListNode> action)
        {
            if (!node.HasChildren) return;
            for (int i = 0; i < node.Nodes.Count; i++)
            {
                var cnode = node.Nodes[i];
                action(cnode);
                RecursionChildNode(cnode, action);
            }
        }

        /// <summary>
        /// 递归整个树节点
        /// </summary>
        /// <param name="action"></param>
        public void RecursionTreeNode(Action<TreeListNode> action)
        {
            foreach (TreeListNode node in Tree.Nodes)
            {
                action(node);
                if (node.HasChildren)
                {
                    RecursionChildNode(node, action);
                }
            }
        }

        /// <summary>
        /// 测试节点的子节点是否有勾选的节点
        /// </summary>
        /// <param name="node">测试节点</param>
        protected bool ChildHasCheck(TreeListNode node)
        {
            foreach (TreeListNode item in node.Nodes)
            {
                if (item.Checked)
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 获取所有父节点
        /// </summary>
        /// <param name="node">节点</param>
        /// <param name="self">是否包含自身</param>
        public List<TreeListNode> GetAllParentNodes(TreeListNode node, bool self = false)
        {
            var nodes = new List<TreeListNode>();
            RecursionParentNode(node, p => { nodes.Add(p); });
            if (self)
            {
                nodes.Add(node);
            }
            return nodes;
        }

        /// <summary>
        /// 获取所有父节点对象
        /// </summary>
        /// <param name="node">节点</param>
        /// <param name="self">是否包含自身</param>
        public List<T> GetAllParentList(TreeListNode node, bool self = false)
        {
            var list = new List<T>();
            RecursionParentNode(node, p => { list.Add((T)Tree.GetDataRecordByNode(p)); });
            if (self)
            {
                list.Add((T)Tree.GetDataRecordByNode(node));
            }
            return list;
        }

        /// <summary>
        /// 获取所有父节点主键
        /// </summary>
        /// <param name="node">节点</param>
        /// <param name="self">是否包含自身</param>
        public string[] GetAllParentIds(TreeListNode node, bool self = false)
        {
            var nodeids = new List<string>();
            RecursionParentNode(node, p => { nodeids.Add(GetRecordId(p)); });
            if (self)
            {
                nodeids.Add(GetRecordId(node));
            }
            return nodeids.ToArray();
        }

        /// <summary>
        /// 获取所有子节点
        /// </summary>
        /// <param name="node">节点</param>
        /// <param name="self">是否包含自身</param>
        public List<TreeListNode> GetAllChildNodes(TreeListNode node, bool self = false)
        {
            var nodes = new List<TreeListNode>();
            if (self)
            {
                nodes.Add(node);
            }
            RecursionChildNode(node, p => { nodes.Add(p); });
            return nodes;
        }

        /// <summary>
        /// 获取所有子节点对象
        /// </summary>
        /// <param name="node">节点</param>
        /// <param name="self">是否包含自身</param>
        public List<T> GetAllChildList(TreeListNode node, bool self = false)
        {
            var list = new List<T>();
            if (self)
            {
                list.Add((T)Tree.GetDataRecordByNode(node));
            }
            RecursionChildNode(node, p => { list.Add((T)Tree.GetDataRecordByNode(p)); });
            return list;
        }

        /// <summary>
        /// 获取所有子节点主键
        /// </summary>
        /// <param name="node">节点</param>
        /// <param name="self">是否包含自身</param>
        public string[] GetAllChildIds(TreeListNode node, bool self = false)
        {
            var nodeids = new List<string>();
            if (self)
            {
                nodeids.Add(GetRecordId(node));
            }
            RecursionChildNode(node, p => { nodeids.Add(GetRecordId(p)); });
            return nodeids.ToArray();
        }

        /// <summary>
        /// 获取选中的节点
        /// </summary>
        public List<TreeListNode> GetCheckedNodes()
        {
            //var selectedList = new List<TreeListNode>();
            //this.RecursionTreeNode(p =>
            //{
            //    if (p.Checked)
            //    {
            //        selectedList.Add(p);
            //    }
            //});
            return Tree.GetAllCheckedNodes();
        }

        #endregion

        /// <summary>
        /// 获取指定行的对象
        /// </summary>
        /// <param name="node">节点</param>
        public T Get(TreeListNode node)
        {
            var rowObject = Tree.GetDataRecordByNode(node);
            return rowObject as T;
            //var view = rowObject as DataRowView;
            //if (view == null) return rowObject as T;
            //var instance = new T();
            //DataTableHelper.ConvertToObject(view.Row, () => instance);
            //return instance;
        }

        /// <summary>
        /// 获取指定行的对象
        /// </summary>
        /// <param name="node">节点</param>
        public T GetParent(TreeListNode node)
        {
            if (node.ParentNode != null)
            {
                return Get(node.ParentNode);
            }
            return null;
        }

        /// <summary>
        /// 获取主键
        /// </summary>
        /// <param name="node">节点对象</param>
        public string GetRecordId(TreeListNode node)
        {
            return node?.GetValue(Tree.KeyFieldName).ToStringOrEmpty();
            //var dataRecord = Tree.GetDataRecordByNode(node);
            //if (dataRecord is DataRowView rowView)
            //{
            //    var row = rowView.Row;
            //    return row[Tree.KeyFieldName].ToStringOrEmpty();
            //}
            //return ObjectHelper.GetObjectValue(dataRecord, Tree.KeyFieldName).ToStringOrEmpty();
        }

        /// <summary>
        /// 根据主键查找节点
        /// </summary>
        /// <param name="id">主键</param>
        public TreeListNode FindNodeByDataId(string id)
        {
            if (string.IsNullOrEmpty(id)) throw new ArgumentNullException(nameof(id));

            var pkType = Metadata.AllColumns[Tree.KeyFieldName].Property.Property.PropertyType;
            var node = Tree.FindNodeByKeyID(ObjectHelper.ConvertObjectValue(id, pkType));
            return node;
        }

        /// <summary>
        /// 根据主键设置节点焦点
        /// </summary>
        /// <param name="id">主键</param>
        public void FocusNodeById(string id)
        {
            var node = FindNodeByDataId(id);
            if (node != null)
            {
                Tree.SetFocusedNode(node);
            }
        }

        #endregion

        #region 拖拽节点

        private void Tree_BeforeDragNode(object sender, BeforeDragNodeEventArgs e)
        {
            dragNode = (TreeListNode)e.Node.Clone();
        }

        private void Tree_AfterDragNode(object sender, NodeEventArgs e)
        {
            if (Metadata.PathProperty == null) return;
            string sortName = Metadata.PathProperty.Name;
            if (string.IsNullOrEmpty(sortName)) return;
            if (e.Node == null || dragNode == null) return;

            string oldParentId = "0";
            string newParentId = "0";
            if (dragNode.ParentNode != null)
            {
                oldParentId = GetRecordId(dragNode.ParentNode);
            }
            if (e.Node.ParentNode != null)
            {
                newParentId = GetRecordId(e.Node.ParentNode);
            }

            if (!oldParentId.Equals(newParentId))//检测拖拽的节点的父节点发送变化了
            {
                //更新老节点顺序
                TreeListNodes oldnodes = dragNode.ParentNode == null ? Tree.Nodes : dragNode.ParentNode.Nodes;
                ChangedSortIndex(oldnodes, sortName);

                //更新新节点顺序
                TreeListNodes newnodes = e.Node.ParentNode == null ? Tree.Nodes : e.Node.ParentNode.Nodes;
                ChangedSortIndex(newnodes, sortName);

                //更新父节点
                AddDataChanged(new PrimaryKeyValue(GetRecordId(e.Node), Tree.ParentFieldName, newParentId));
            }
            else //在同级中改变顺序,父节点不变
            {
                TreeListNodes nodes = e.Node.ParentNode == null ? Tree.Nodes : e.Node.ParentNode.Nodes;
                ChangedSortIndex(nodes, sortName);
            }
            OnModifyData(EventArgs.Empty);
        }

        private void ChangedSortIndex(TreeListNodes nodes, string sortName)
        {
            foreach (TreeListNode item in nodes)
            {
                AddDataChanged(new PrimaryKeyValue(GetRecordId(item), sortName, GetNodeSortPath(item)));
            }
        }

        #endregion

        #region 复选框

        void Tree_AfterCheckNode(object sender, DevExpress.XtraTreeList.NodeEventArgs e)
        {
            if (AutoCheckedParentNode)
            {
                CheckedParentNodes(e.Node, p =>
                {
                    p.Checked = ChildHasCheck(p);
                    //SetInnerCheckedNode(p);
                });
            }
            if (AutoCheckedChildNode)
            {
                var nodeChecked = e.Node.Checked;
                CheckedChildNodes(e.Node, p =>
                {
                    p.Checked = nodeChecked;
                    //SetInnerCheckedNode(p);
                });
            }
            Tree.SetFocusedNode(e.Node);
            //SetInnerCheckedNode(e.Node);
            OnNodeChecked(EventArgs.Empty);
        }

        ///// <summary>
        ///// 删除勾选的节点
        ///// </summary>
        //public void DeleteCheckedNodes()
        //{
        //    var nodes = GetCheckedNodes();
        //    foreach (var item in nodes)
        //    {
        //        Tree.DeleteNode(item);
        //    }
        //    Tree.UncheckAll();
        //    ClearInnerCheckedNode();
        //}

        /// <summary>
        /// 获取选中的主键数组
        /// </summary>
        public string[] GetSelectedIds()
        {
            var selectedNodes = Tree.Selection;
            string[] ids = new string[selectedNodes.Count];
            for (int i = 0; i < selectedNodes.Count; i++)
            {
                ids[i] = GetRecordId(selectedNodes[i]);
            }
            return ids;
        }

        /// <summary>
        /// 获取勾选的主键数组
        /// </summary>
        public string[] GetCheckedIds()
        {
            var selectedNodes = GetCheckedNodes();
            var ids = new string[selectedNodes.Count];
            for (int i = 0; i < selectedNodes.Count; i++)
            {
                ids[i] = GetRecordId(selectedNodes[i]);
            }
            return ids;
        }

        /// <summary>
        /// 设置需要勾选的主键集合
        /// </summary>
        /// <param name="ids">主键集合</param>
        public void SetCheckedNodeIds(IEnumerable ids)
        {
            foreach (var item in ids)
            {
                var node = Tree.FindNodeByKeyID(item);
                if (node != null)
                {
                    node.Checked = true;
                }
            }
        }

        ///// <summary>
        ///// 设置内部复选状态
        ///// </summary>
        ///// <param name="node">节点</param>
        //public void SetInnerCheckedNode(TreeListNode node)
        //{
        //    string id = GetRecordId(node);
        //    if (node.Checked)
        //    {
        //        _checkedKeyDatas[id] = id;
        //    }
        //    else
        //    {
        //        _checkedKeyDatas.Remove(id);
        //    }
        //}

        ///// <summary>
        ///// 清空内部复选状态
        ///// </summary>
        //public void ClearInnerCheckedNode()
        //{
        //    _checkedKeyDatas.Clear();
        //}

        #endregion

        #region SortPath


        /// <summary>
        /// 获取节点的排序路径
        /// </summary>
        /// <param name="node">节点</param>
        public string GetNodeSortPath(TreeListNode node)
        {
            return GetNodeSortPathUnit(node == null ? 0 : Tree.GetNodeIndex(node));
        }

        /// <summary>
        /// 获取新建节点排序路径
        /// </summary>
        /// <param name="parentNode">父节点</param>
        public string NewNodeSortPath(TreeListNode parentNode)
        {
            var treeListNodes = parentNode == null ? Tree.Nodes : parentNode.Nodes;
            if (treeListNodes == null || treeListNodes.Count == 0)
            {
                return GetNodeSortPathUnit(0);
            }
            return GetNodeSortPathUnit(treeListNodes.Count);
        }

        /// <summary>
        /// 获取单个节点排序路径
        /// </summary>
        /// <param name="sortIndex">序号</param>
        public string GetNodeSortPathUnit(int sortIndex)
        {
            return StringHelper.GetFixedLengthString(sortIndex.ToString(), 4, "0", true);
        }

        ///// <summary>
        ///// 获取节点的父节点路径
        ///// </summary>
        ///// <param name="node">节点</param>
        //public string GetNodeParentPath(TreeListNode node)
        //{
        //    if (node == null)
        //    {
        //        return "0";
        //    }
        //    if (node.ParentNode == null)
        //    {
        //        return GetRecordId(node);
        //    }
        //    var parentPath = new StringBuilder();
        //    GetNodeParentPathCore(node.ParentNode, parentPath);
        //    return parentPath.ToString();
        //}

        //private void GetNodeParentPathCore(TreeListNode parentNode, StringBuilder parentPath)
        //{
        //    string parentId = this.GetRecordId(parentNode);
        //    if (parentPath.Length == 0)
        //    {
        //        parentPath.Append(parentId);
        //    }
        //    else
        //    {
        //        parentPath.Insert(0, string.Concat(parentId, ","));
        //    }

        //    if (parentNode.ParentNode != null)
        //    {
        //        GetNodeParentPathCore(parentNode.ParentNode, parentPath);
        //    }
        //}

        #endregion

        #region DataChanged

        /// <summary>
        /// 添加改变的列值
        /// </summary>
        /// <param name="item">改变项</param>
        private void AddDataChanged(PrimaryKeyValue item)
        {
            string id = item.Id;
            if (string.IsNullOrEmpty(id)) return;
            _changedDatas[id + item.Key] = item;
        }

        /// <summary>
        /// 清空变化的值列表
        /// </summary>
        public void ClearDataChanged()
        {
            _changedDatas.Clear();
        }

        /// <summary>
        /// 获取改变的数据
        /// </summary>
        /// <returns></returns>
        public List<PrimaryKeyValue> GetChangedDataList()
        {
            this.Tree.PostEditor();
            var list = new List<PrimaryKeyValue>();
            foreach (var item in _changedDatas)
            {
                list.Add(item.Value);
            }
            return list;
        }

        private void Tree_CellValueChanged(object sender, CellValueChangedEventArgs e)
        {
            if (e.Node == null || Metadata == null || string.IsNullOrEmpty(Metadata.PrimaryProperty.Name)) return;
            string columnName = e.Column.FieldName;
            string pk = GetRecordId(e.Node);
            if (pk.IsEmpty()) return;
            string columnValue = e.Value.ToStringOrEmpty();
            AddDataChanged(new PrimaryKeyValue(pk, columnName, columnValue));
            OnModifyData(EventArgs.Empty);
        }

        #endregion
    }
}